﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using ExFlash.Records;
using System.Drawing.Drawing2D;
using ExFlash.Tags;

namespace ExFlash.Controls
{
    public enum WindingDirection
    {
        Clockwise,
        CounterClockWise
    }

    public partial class ShapeViewer : UserControl
    {
        private RectangleRecord _frameSize;
        private DefineShapeTag _shape;

        public RectangleRecord FrameSize
        {
            get { return _frameSize; }
            set { _frameSize = value; }
        }
        

        /// <summary>
        /// Gets or Sets the shape to draw.
        /// </summary>
        public DefineShapeTag ShapeTag
        {
            get
            {
                return _shape;
            }
            set
            {
                _shape = value;

                Refresh();
            }
        }

        public ShapeViewer()
            : this(null)
        {
        }

        public ShapeViewer(RectangleRecord frameSize)
            : this (frameSize, null)
        {
            
        }

        public ShapeViewer(RectangleRecord frameSize, DefineShapeTag shape)
        {
            InitializeComponent();

            if (frameSize != null)
            {
                _frameSize = (RectangleRecord)frameSize.Clone();
            }

            ShapeTag = shape;
        }

        private void AddTwips(ref Point point, int x, int y)
        {
            float pixelX = x / 20.0f;
            float pixelY = y / 20.0f;

            int addX = (int)( (pixelX / (_frameSize.Xmax / 20.0f)) * Width);
            int addY = (int)((pixelY / (_frameSize.Ymax / 20.0f)) * Height);

            point.X += addX;
            point.Y += addY;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            try
            {
                if (ShapeTag == null || _frameSize == null)
                {
                    return;
                }

                ShapeWithStyle shape = ShapeTag.Shapes;

                Dictionary<StyleChangeRecord, GraphicsPath> shapeDictionary = new Dictionary<StyleChangeRecord, GraphicsPath>();

                GraphicsPath curPath = null;
                Point curPos = new Point(Height / 2, Width / 2);

                foreach (ShapeRecord rec in shape.ShapeRecords)
                {
                    if (rec is StyleChangeRecord)
                    {
                        StyleChangeRecord styleRec = (StyleChangeRecord)rec;

                        if (styleRec.StateMoveTo || styleRec.StateNewStyles || styleRec.StateLineStyle || styleRec.StateFillStyle1 || styleRec.StateFillStyle0)
                        {
                            if (styleRec.StateMoveTo)
                            {
                                AddTwips(ref curPos, styleRec.MoveDeltaX, styleRec.MoveDeltaY);
                            }

                            curPath = new GraphicsPath();
                            shapeDictionary.Add(styleRec, curPath);
                        }
                    }
                    else if (rec is StrightEdgeRecord)
                    {
                        StrightEdgeRecord edgeRec = (StrightEdgeRecord)rec;

                        Point start = curPos;

                        AddTwips(ref curPos, edgeRec.DeltaX, edgeRec.DeltaY);

                        if (curPath != null)
                        {
                            curPath.AddLine(start, curPos);
                        }
                    }
                    else if (rec is CurvedEdgeRecord)
                    {
                        CurvedEdgeRecord edgeRec = (CurvedEdgeRecord)rec;

                        Point start = curPos;

                        AddTwips(ref start, (int)(edgeRec.ControlDeltaX * 2.0f / 3.0f), (int)(edgeRec.ControlDeltaY * 2.0f / 3.0f));

                        Point first = curPos;

                        AddTwips(ref start, (int)(edgeRec.ControlDeltaX * 1.0f / 3.0f), (int)(edgeRec.ControlDeltaY * 1.0f / 3.0f));

                        AddTwips(ref start, (int)(edgeRec.AnchorDeltaX * 1.0f / 3.0f), (int)(edgeRec.AnchorDeltaY * 1.0f / 3.0f));

                        Point second = curPos;

                        AddTwips(ref start, (int)(edgeRec.AnchorDeltaX * 2.0f / 3.0f), (int)(edgeRec.AnchorDeltaY * 2.0f / 3.0f));

                        if (curPath != null) curPath.AddBezier(start, first, second, curPos);
                    }
                }

                if (curPath != null) curPath.CloseFigure();

                FillStyleArray fillArray = shape.FillStyles;
                LineStyleArray lineArray = shape.LineStyles;

                FillStyleRecord currentFill0 = null;
                FillStyleRecord currentFill1 = null;
                LineStyleRecord currentLine = null;

                foreach (var kvp in shapeDictionary)
                {
                    StyleChangeRecord rec = kvp.Key;

                    if (rec.StateNewStyles)
                    {
                        fillArray = rec.FillStyles;
                        lineArray = rec.LineStyles;
                    }

                    if (rec.StateFillStyle0)
                    {
                        currentFill0 = (rec.FillStyle0) == 0 ? null : fillArray[(int)rec.FillStyle0 - 1];
                    }

                    if (rec.StateFillStyle1)
                    {
                        currentFill1 = (rec.FillStyle1 == 0) ? null : fillArray[(int)rec.FillStyle1 - 1];
                    }

                    if (rec.StateLineStyle)
                    {
                        currentLine = (rec.LineStyle == 0) ? null : lineArray[(int)rec.LineStyle - 1];
                    }

                    if (currentLine != null)
                    {
                        Pen pen = currentLine.GetPen();
                        e.Graphics.DrawPath(pen, kvp.Value);
                        pen.Dispose();
                    }

                    //Brush brush;

                    if (DetermineWindingDirection(kvp.Value.PathPoints) == WindingDirection.Clockwise)
                    {
                        if (currentFill1 != null)
                        {
                            try
                            {
                                //brush = CurrentFill1.GetBrush(kvp.Value);
                            }
                            catch
                            {
                                continue;
                            }

                            //e.Graphics.FillPath(brush, kvp.Value);
                            //brush.Dispose();
                        }
                    }
                    else
                    {
                        if (currentFill0 != null)
                        {
                            try
                            {
                                //brush = CurrentFill0.GetBrush(kvp.Value);
                            }
                            catch
                            {
                                continue;
                            }

                            //e.Graphics.FillPath(brush, kvp.Value);
                            //brush.Dispose();
                        }
                    }
                    
                }
            }
            catch (Exception)
            {
            }
        }

        private WindingDirection DetermineWindingDirection(PointF[] polygon)
        {
            // find a point in the middle
            float middleX = polygon.Average(p => p.X);
            float middleY = polygon.Average(p => p.Y);
            var pointInPolygon = new PointF(middleX, middleY);
            //Console.WriteLine("MiddlePoint = {0}", pointInPolygon);

            double w = 0;
            var points = polygon.Select(point =>
            {
                var newPoint = new PointF(point.X - pointInPolygon.X, point.Y - pointInPolygon.Y);
                //Console.WriteLine("New Point: {0}", newPoint);
                return newPoint;
            }).ToList();

            for (int i = 0; i < points.Count; i++)
            {
                var secondPoint = i + 1 == points.Count ? 0 : i + 1;
                double x = points[i].X;
                double xp1 = points[secondPoint].X;
                double Y = points[i].Y;
                double yp1 = points[secondPoint].Y;

                if (Y * yp1 < 0)
                {
                    double r = x + ((Y * (xp1 - x)) / (Y - yp1));
                    if (r > 0)
                        if (Y < 0)
                            w = w + 1;
                        else
                            w = w - 1;
                }
// ReSharper disable CompareOfFloatsByEqualityOperator
                else if ((Y == 0) && (x > 0))
// ReSharper restore CompareOfFloatsByEqualityOperator
                {
                    if (yp1 > 0)
                        w = w + .5;
                    else
                        w = w - .5;
                }
// ReSharper disable CompareOfFloatsByEqualityOperator
                else if ((yp1 == 0) && (xp1 > 0))
// ReSharper restore CompareOfFloatsByEqualityOperator
                {
                    if (Y < 0)
                        w = w + .5;
                    else
                        w = w - .5;
                }
            }
            return w > 0 ? WindingDirection.Clockwise : WindingDirection.CounterClockWise;
        }
    }
}
